package Z09Swing.SwingThred;
        import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;


/**
 * @author 李某
 * * @date 2021/10/21
 * *物有本末，事有终始。知其先后者，则近道矣！
 * *荷花开满池塘的前一天还是半塘荷花半塘云。
 */
public class Threed线程 {
}

/*
在Swing程序的开发中，需要建立3种线程的概念
1. 初始化线程
    初始化线程用于创建各种容器，
    组件并显示他们，一旦创建并显示，初始化线程的任务就结束了。

2. 事件调度线程
    通过事件监听的学习，我们了解到Swing是一个事件驱动的模型，
    所有和事件相关的操作都放是放在事件调度线程 (Event Dispatch)中进行的。
    比如点击一个按钮，对应的ActionListener.actionPerformed 方法中的代码，
    就是在事件调度线程 Event Dispatch Thread中执行的。

3. 长耗时任务线程
    有时候需要进行一些长时间的操作，
    比如访问数据库，文件复制，连接网络，统计文件总数等等。
    这些操作就不适合放在事件调度线程中进行，
    因为占用时间久了，会让使用者感觉界面响应很卡顿。
    为了保持界面响应的流畅性,所有长耗时任务都应该放在专门的
    长耗时任务线程中进行
 */


/*
    在开始讲解这3种线程之前，
    要建立一个概念： 事件调度线程是单线程的。
    为什么呢？

    这是因为 Swing里面的各种组件类，
    比如JTextField,JButton 都不是线程安全的，
    这就意味着，如果有多个线程，那么同一个JTextField的setText方法，
    可能会被多个线程同时调用，这会导致同步问题以及错误数据的发生。

    如果把组件类设计成为线程安全的，由于Swing事件调度的复杂性，
    就很有可能导致死锁的发生。

    为了规避同步问题，以及降低整个Swing设计的复杂度，
    提高Swing的相应速度，Swing中的
    事件调度线程被设计成为了单线程模式，即只有一个线程在负责事件的响应工作。
 */

/*
    如代码所示，同时我们在初始化一个图形界面的时候，
    都会直接在主方法的主线程里，直接调用如下代码来进行初始化

    new TestFrame().setVisible(true);

    如果是小程序这没有什么问题，如果是复杂的程序就有可能产生问题了。
    因为这里有两个线程在同时访问组件：1. 主线程 2. 事件调度线程。
    如果是复杂的图形界面程序，就有可能出现这两个线程同时操作的情况，导致同步问题的产生。

    为了规避这个问题的产生，创建和显示界面的工作，
    最好也交给事件调度线程，这样就保证了只有一个线程在访问这些组件


    SwingUtilities.invokeLater(new Runnable() {
    public void run() {
        new TestFrame().setVisible(true);
    }
    });

    像这样，
    new TestFrame().setVisible(true);
    这段代码就是在事件调度线程中执行了。

    还可以使用
    SwingUtilities.isEventDispatchThread()
    来判断当前线程是否是事件调度线程
 */

class test {
}

class TestGUI1 {
    public static void main(String[] args) {
        new TestFrame().setVisible(true);

//		SwingUtilities.invokeLater(new Runnable() {
//			public void run() {
//				new TestFrame().setVisible(true);
//			}
//		});

    }

    static class TestFrame extends JFrame {
        public TestFrame() {
            setTitle("LoL");

            setSize(400, 300);

            setLocation(200, 200);

            setLayout(null);

            JButton b = new JButton("一键秒对方基地挂");

            b.setBounds(50, 50, 280, 30);

            add(b);

            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

            setVisible(true);

            System.out.println("当前线程是否是 事件调度线程: " + SwingUtilities.isEventDispatchThread());

        }
    }
}


/*
    以 按钮监听 中的代码为例，
    ActionListener.actionPerformed
    中的代码，就是事件调度线程执行的。

    可以借助SwingUtilities.isEventDispatchThread()
    确认，是事件调度线程在执行相应的代码

    那么事件调度线程又是如何去执行这段代码的呢？
 */


class TestGUI {
    public static void main(String[] args) {

        JFrame f = new JFrame("LoL");
        f.setSize(400, 300);
        f.setLocation(580, 200);
        f.setLayout(null);

        final JLabel l = new JLabel();

        ImageIcon i = new ImageIcon("e:/project/j2se/shana.png");
        l.setIcon(i);
        l.setBounds(50, 50, i.getIconWidth(), i.getIconHeight());

        JButton b = new JButton("隐藏图片");
        b.setBounds(150, 200, 100, 30);

        b.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                l.setVisible(false);

                System.out.println("当前使用的是事件调度线程：" + SwingUtilities.isEventDispatchThread());
            }
        });

        f.add(l);
        f.add(b);
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        f.setVisible(true);
    }
}


/*
    有时候需要执行长耗时任务，比如数据库查询，文件复制，访问网络等等。
    而这些操作一般都会在事件响应后发起，就会自动进入事件调度线程。
    而事件调度线程又是单线程模式，其结果就会是在执行这些长耗时任务的时候，界面就无响应了。

    如图所示，当点击第一个按钮的时候，会在其中进行一个5秒钟的任务，
    这个期间，第一个按钮会保持按下状态，其他按钮也无法点击，出现了无响应了状态。

    为了解决这个问题，Swing提供了一个SwingWorker类来解决。
    SwingWorker是一个抽象类，为了使用，
    必须实现方法 doInBackground，
    在doInBackground中，就可以编写我们的任务，
    然后执行SwingWorker的execute方法，放在专门的工作线程中去运行。

    SwingWorker worker = new SwingWorker() {
    protected Object doInBackground() throws Exception {
        //长耗时任务
        return null;
    }
    };
    worker.execute();
    SwingWorker又是如何工作的呢？
    当SwingWorker执行execute的时候，调用默认有10根线程的线程池，
    执行doInBackground中的代码，通过如下代码，
    可以获知执行当前SwingWorder的线程名称
 */